package cn.wen.yinxiu.works.service.impl;

import cn.wen.yinxiu.common.cache.DistributedCache;
import cn.wen.yinxiu.common.constant.Constant;
import cn.wen.yinxiu.common.constant.RedisConstants;
import cn.wen.yinxiu.common.convention.errorcode.ErrorCode;
import cn.wen.yinxiu.common.convention.exception.ClientException;
import cn.wen.yinxiu.common.enums.DelEnum;
import cn.wen.yinxiu.common.enums.StatusEnum;
import cn.wen.yinxiu.common.to.user.UserTO;
import cn.wen.yinxiu.common.toolkit.IPUtils;
import cn.wen.yinxiu.common.toolkit.RequestHolder;
import cn.wen.yinxiu.works.entity.WorksEntity;
import cn.wen.yinxiu.works.feign.UserFeignService;
import cn.wen.yinxiu.works.service.WorksService;
import cn.wen.yinxiu.works.vo.AddCommentVO;
import cn.wen.yinxiu.works.vo.CommentVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.wen.yinxiu.common.toolkit.PageUtils;
import cn.wen.yinxiu.common.toolkit.Query;

import cn.wen.yinxiu.works.dao.CommentDao;
import cn.wen.yinxiu.works.entity.CommentEntity;
import cn.wen.yinxiu.works.service.CommentService;
import org.springframework.transaction.annotation.Transactional;


@Component
@Service("commentService")
public class CommentServiceImpl extends ServiceImpl<CommentDao, CommentEntity> implements CommentService {

    @Value("${yinxiu.pageSize}")
    private Integer pageSize;

    @Autowired
    private CommentDao commentDao;

    @Autowired
    private WorksService worksService;

    @Autowired
    private UserFeignService userFeignService;

    @Autowired
    private DistributedCache distributedCache;

    @Autowired
    private ThreadPoolExecutor executor;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CommentEntity> page = this.page(
                new Query<CommentEntity>().getPage(params),
                new QueryWrapper<CommentEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public Long getCommentNumByWorksId(Long worksId) {
        QueryWrapper<CommentEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("works_id", worksId);
        queryWrapper.eq("status", StatusEnum.NORMAL_USE.code());
        queryWrapper.eq("is_deleted", DelEnum.NORMAL.code());
        Long count = commentDao.selectCount(queryWrapper);
        return count;
    }

    @Override
    public Long getCommentTotalNum() {
        // 评论状态为正常使用
        QueryWrapper<CommentEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", StatusEnum.NORMAL_USE.code());
        queryWrapper.eq("is_deleted", DelEnum.NORMAL.code());
        return commentDao.selectCount(queryWrapper);
    }

    private Map<Long, Long> likeCommentMap = new HashMap<>();

    @Override
    public List<CommentVO> listCommentByWorksId(Long worksId, Integer page, Long userId) {
        // 获取楼主的数据，通过评论时间来排列 同时分页
        List<CommentVO> comments = new ArrayList<>();

        // 1.异步获取所有的父评论列表
        CompletableFuture<Void> commentsFuture = CompletableFuture.runAsync(() -> {
           comments.addAll(commentDao.findByWorksIdAndParentCommentNull(worksId, StatusEnum.NORMAL_USE.code(),
                    DelEnum.NORMAL.code(), (page - 1) * pageSize, pageSize));
        }, executor);

        Map<Long, Long> likeMap = new HashMap<>();
        // 2.获取当前用户已点赞的评论ID
        CompletableFuture<Void> likeFuture = CompletableFuture.runAsync(() -> {
            Map<Long, Long> worksMap = commentDao.getUserCommentLikeMap(userId, worksId);
            likeMap.putAll(worksMap);
        }, executor);
        likeCommentMap = likeMap;
        // 等待所有任务完成
        try {
            CompletableFuture.allOf(commentsFuture, likeFuture).get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        optimizeComment(comments);
        List<CommentVO> commentVOS = eachComment(comments);
        likeCommentMap = new HashMap<>();
        return commentVOS;
    }

    @Transactional
    @Override
    public boolean insertComment(AddCommentVO addCommentVO) {
        CommentEntity target = new CommentEntity();
        BeanUtils.copyProperties(addCommentVO, target);
        // 通过worksId获取 当前作品的UserId信息
        WorksEntity worksEntity = worksService.getWorksInfoById(target.getWorksId());
        // 设置是否为博主
        target.setIsOwner(worksEntity.getUserId() == target.getUserId() ? 1 : 0);
        target.setStatus(StatusEnum.NORMAL_USE.code());
        // 获取当前用户信息
        UserTO user = userFeignService.getUserInfoById(target.getUserId()).getData();
        target.setCommentAvatar(user.getAvatar());
        target.setCommentNickname(user.getUsername());
        // 设置基本信息
        if (addCommentVO.getParentId() == 0) {
            target.setLevel(1);
        }
        target.setIsDeleted(DelEnum.NORMAL.code());
        // 设置评论用户的IP地址
        //从Redis中获取IP来源
        String ip = IPUtils.getIpAddr(RequestHolder.getRequest());
        String jsonResult = distributedCache.get(RedisConstants.IP_SOURCE + Constant.SYMBOL_COLON + ip, String.class);
        if (StringUtils.isEmpty(jsonResult)) {
            String addresses = IPUtils.getAddresses("ip" + "=" + ip, "UTF-8");
            if (StringUtils.isNotEmpty(addresses)) {
                target.setCommentIp(addresses);
                distributedCache.put(RedisConstants.IP_SOURCE + Constant.SYMBOL_COLON + ip, addresses, 24, TimeUnit.HOURS);
            } else {
                target.setCommentIp("未知IP");
            }
        } else {
            target.setCommentIp(jsonResult);
        }
        int insert = commentDao.insert(target);
        // 修改作品的评论数量
        worksService.updateWorksCommentNum(worksEntity.getId(), Constant.NUM_ONE);
        return insert > 0;
    }

    @Override
    public boolean deleteCommentById(Long commendId, Long worksId) {
        // 判断ID是否存在
        CommentEntity commentEntity = commentDao.selectById(worksId);
        if (commentEntity == null) throw new ClientException(ErrorCode.CLIENT_PARAMS_ERROR);
        CommentEntity target = new CommentEntity();
        target.setCommentId(commendId);
        target.setIsDeleted(DelEnum.DELETE.code());
        boolean b = commentDao.updateById(target) > 0;
        // 修改作品的评论数量
        worksService.updateWorksCommentNum(worksId, -1);
        return b;
    }

    @Override
    public Long getCommentCountByStatus(int status) {
        QueryWrapper<CommentEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", status);
        queryWrapper.eq("is_deleted", DelEnum.NORMAL.code());
        return commentDao.selectCount(queryWrapper);
    }

    @Override
    public boolean commentLikeById(Long commendId, Long worksId, Long userId) {
        return commentDao.addCommentLikeById(commendId, worksId, userId);
    }

    @Override
    public boolean commentUnlikeById(Long commendId, Long worksId, Long userId) {
        return commentDao.deleteCommentLikeById(commendId, worksId, userId);
    }

    private void optimizeComment(List<CommentVO> comments) {
        for (CommentVO comment : comments) {
            comment.setInOpen(false);
            comment.setInReply(false);
            comment.setLike(likeCommentMap.get(comment.getCommentId()) != null ? true : false);
            comment.setReplyComments(commentDao.listTreeComment(comment.getCommentId()
            ));
        }
    }


    /**
     * 循环每个顶级的评论节点
     *
     * @param comments
     * @return
     */
    private List<CommentVO> eachComment(List<CommentVO> comments) {
        List<CommentVO> commentsView = new ArrayList<>();
        for (CommentVO comment : comments) {
            CommentVO c = new CommentVO();
            BeanUtils.copyProperties(comment, c);
            // 存储全部的楼主评论
            commentsView.add(c);
        }
        // 合并评论的各层子代到第一级子代集合中
        combineChildren(commentsView);
        return commentsView;
    }

    //存放迭代找出的所有子代的集合
    private List<CommentVO> tempReply = new ArrayList<>();

    /**
     * @param comments root根节点，blog不为空的对象集合
     * @return
     */
    private void combineChildren(List<CommentVO> comments) {
        for (CommentVO comment : comments) {
            // 获取子列表
            List<CommentVO> replyComments = comment.getReplyComments();
            for (CommentVO replyComment : replyComments) {
                // 循环迭代，找出只子代，将子代存储到当前的二级列表中
                recursively(replyComment);
            }
            // 修改顶级的子节点设置处理过后的数据
            comment.setReplyComments(tempReply);
            // 清空暂存区
            tempReply = new ArrayList<>();
        }
    }

    /**
     * 递归迭代，剥洋葱
     *
     * @param comment 被迭代的对象
     * @return
     */
    private void recursively(CommentVO comment) {
        beanCopyProperties(comment);
        // 然后判断是否还有子评论 如果存在回复 则再次的调用递归
        if (comment.getReplyComments().size() > 0) {
            List<CommentVO> replyComments = comment.getReplyComments();
            for (CommentVO replyComment : replyComments) {
                beanCopyProperties(replyComment);
                if (replyComment.getReplyComments().size() > 0) {
                    recursively(replyComment);
                }
            }
        }
    }

    private void beanCopyProperties(CommentVO comment) {
        CommentVO commentOne = new CommentVO();
        BeanUtils.copyProperties(comment, commentOne);
        commentOne.setReplyComments(null);
        commentOne.setInReply(false);
        commentOne.setLike(likeCommentMap.get(comment.getCommentId()) != null ? true : false);
        // 先将该子回复添加到列表中
        tempReply.add(commentOne);
    }


}